package net.sansi.v3correctserver.service.photorec.rec;

import lombok.extern.slf4j.Slf4j;
import net.sansi.v3correctserver.entity.IntPoint;
import net.sansi.v3correctserver.entity.image.RecImage;
import net.sansi.v3correctserver.util.RGBUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Fang
 * create time : 2024/12/13
 * IDE : IntelliJ IDEA
 * <p>
 * 识别围栏位置
 * 4个顶点
 */
@Slf4j
public class RecFence {

    // 亮灯的相片
    private final RecImage brightPhoto;

    // 暗灯背景的相片
    private final RecImage blackPhoto;

    // 方向 [ 左上， 右上， 右下， 左下]
    int[] DIR_X = {-1, 1, 1, -1};
    int[] DIR_Y = {-1, -1, 1, 1};

    // 基准亮度
    double baseBrightness = 0;

    // 图片宽高
    int w = 0, h = 0;

    /**
     * 构造函数
     *
     * @param brightPhoto 亮灯的相片
     * @param blackPhoto  暗灯背景的相片
     */
    public RecFence(RecImage brightPhoto, RecImage blackPhoto) {
        this.brightPhoto = brightPhoto;
        this.blackPhoto = blackPhoto;
        w = brightPhoto.getWidth();
        h = brightPhoto.getHeight();
    }

    /**
     * 搜索围栏
     * 带4个顶点的围栏
     *
     * @return 点列表
     */
    public List<IntPoint> searchFence() {
        List<IntPoint> fence = new ArrayList<>(4);

        int[] start = {w / 2, h / 2};
        baseBrightness = Math.pow(getBrightness(start[0], start[1]), 1.37 / 2);

        for (int i = 0; i < 4; i++) {
            fence.add(null);
            searchTopPoint(start[0], start[1], i, fence);
        }

        for (int i = 0; i < 4; i++) {
            IntPoint p = fence.get(i);
            p.setX(Math.max(0, Math.min(w, p.getX() + DIR_X[i] * 10)));
            p.setY(Math.max(0, Math.min(h, p.getY() + DIR_Y[i] * 10)));
        }
        log.info("[围栏计算] 结果 {}", fence);
        return fence;
    }

    /**
     * 搜索某个方向上的极限顶点
     *
     * @param startX 开始点 x
     * @param startY 开始点 y
     * @param dir    方向
     * @param fence  点列表
     */
    private void searchTopPoint(int startX, int startY, int dir, List<IntPoint> fence) {
        IntPoint p0 = new IntPoint(startX, startY);

        if (!isInPhoto(p0)) return;

        IntPoint px = new IntPoint(startX, startY);
        IntPoint py = new IntPoint(startX, startY);

        while (px.getX() > -1 && px.getX() < w) {
            px.setX(px.getX() + DIR_X[dir]);
            if (px.getX() < 0 || px.getX() >= w || getBrightness(px) < baseBrightness) break;
        }

        while (py.getY() > -1 && py.getY() < h) {
            py.setY(py.getY() + DIR_Y[dir]);
            if (py.getY() < 0 || py.getY() >= h || getBrightness(py) < baseBrightness) break;
        }

        px.setX(Math.max(0, Math.min(w - 1, px.getX())));
        py.setY(Math.max(0, Math.min(h - 1, py.getY())));

        IntPoint cp = new IntPoint(
                (px.getX() + startX) >> 1,
                (py.getY() + startY) >> 1
        );

        fence.set(dir, cp);
        if (distance(p0, cp) < 1 || getBrightness(cp) < baseBrightness) return;

        searchTopPoint(cp.getX(), cp.getY(), dir, fence);
    }

    /**
     * 计算两点距离
     *
     * @param p1 点1
     * @param p2 点2
     * @return 距离
     */
    int distance(IntPoint p1, IntPoint p2) {
        return Math.abs(p1.getX() - p2.getX()) + Math.abs(p1.getY() - p2.getY());
    }

    /**
     * 判断点是否在图片内
     *
     * @param x x 坐标
     * @param y y 坐标
     * @return 是否在图片内
     */
    private boolean isInPhoto(int x, int y) {
        return x >= 0 && x < w && y >= 0 && y < h;
    }

    /**
     * 判断点是否在图片内
     *
     * @param p 点
     * @return 是否在图片内
     */
    private boolean isInPhoto(IntPoint p) {
        return isInPhoto(p.getX(), p.getY());
    }

    /**
     * 获取亮度
     *
     * @param x x 坐标
     * @param y y 坐标
     * @return 亮度
     */
    private double getBrightness(int x, int y) {
        return RGBUtil.getBrightness(brightPhoto, blackPhoto, x, y);
    }

    /**
     * 获取亮度
     *
     * @param p 点
     * @return 亮度
     */
    private double getBrightness(IntPoint p) {
        return getBrightness(p.getX(), p.getY());
    }
}
